#include "const/oPreDefine.h"
#include "physics/oWorld.h"
#include "physics/oBodyDef.h"
#include "physics/oBody.h"
#include "physics/oGroup.h"
#include "physics/oSensor.h"
#include "event/oEvent.h"
#include "event/oListener.h"
#include "misc/oKeyboard.h"

struct oSensorPair
{
	oSensor* sensor;
	oBody* body;
	void retain()
	{
		sensor->retain();
		body->retain();
	}
	void release()
	{
		sensor->release();
		body->release();
	}
};

static vector<oSensorPair> g_sensorEnters;
static vector<oSensorPair> g_sensorLeaves;
static vector<oBody*> g_queryResults;

class oQueryAABB: public b2QueryCallback
{
public:
	virtual bool ReportFixture( b2Fixture* fixture ) 
	{
		if (!fixture->IsSensor())
		{
			oBody* body = (oBody*)fixture->GetBody()->GetUserData();
			g_queryResults.push_back(body);
		}
		return true;
	}
};
static oQueryAABB g_queryCallback;

oWorld::oWorld():
_world(b2Vec2(0,-10)),
_velocityIterations(10),
_positionIterations(8)
{
	_world.SetContactFilter(&_contactFilter);
	_world.SetContactListener(&_contactListner);
	_listener = oEvent::addListener(oGroup::EVENT_REFILTER, oEventHandler(&oWorld::onRefilter, this));
	_listener->retain();
}

oWorld::~oWorld()
{
	_listener->release();
	for (b2Body* b = _world.GetBodyList();b;b = b->GetNext())
	{
		oBody* body = (oBody*)b->GetUserData();
		if (body)
		{
			body->_bodyB2 = nullptr;
			body->_sensors.removeAllObjects();
		}
	}
}

bool oWorld::init()
{
	if (!CCLayer::init())
	{
		return false;
	}
	this->scheduleUpdateWithPriority(1);
	this->setIsKeypadEnabled(true);
	return true;
}

b2World* oWorld::getB2World()
{
	return &_world;
}

void oWorld::setIterations( int velocityIter, int positionIter )
{
	_velocityIterations = velocityIter;
	_positionIterations = positionIter;
}

void oWorld::setGravity( const b2Vec2& gravity )
{
	_world.SetGravity(gravity);
}

b2Vec2 oWorld::getGravity() const
{
	return _world.GetGravity();
}

void oWorld::update( ccTime dt )
{
	CCLayer::update(dt);
	_world.Step(dt, _velocityIterations, _positionIterations);
	for (b2Body* b = _world.GetBodyList();b;b = b->GetNext())
	{
		oBody* body = (oBody*)b->GetUserData();
		body->updateTransform();
	}
	if (!g_sensorEnters.empty())
	{
		BOOST_FOREACH(oSensorPair& pair, g_sensorEnters)
		{
			if (pair.sensor->getEnable())
			{
				pair.sensor->add(pair.body);
			}
			pair.release();
		}
		g_sensorEnters.clear();
	}
	if (!g_sensorLeaves.empty())
	{
		BOOST_FOREACH(oSensorPair& pair, g_sensorLeaves)
		{
			if (pair.sensor->getEnable())
			{
				pair.sensor->remove(pair.body);
			}
			pair.release();
		}
		g_sensorLeaves.clear();
	}
}

void oWorld::queryAABB( const b2AABB& aabb, function<void(oBody*)> func )
{
	b2AABB b2aabb;
	b2aabb.lowerBound.Set(b2Val(aabb.lowerBound.x), b2Val(aabb.lowerBound.y));
	b2aabb.upperBound.Set(b2Val(aabb.upperBound.x), b2Val(aabb.upperBound.y));
	g_queryResults.clear();
	_world.QueryAABB(&g_queryCallback, b2aabb);
	BOOST_FOREACH(oBody* body, g_queryResults)
	{
		func(body);
	}
}

bool oWorld::isLocked() const
{
	return _world.IsLocked();
}

void oWorld::onRefilter( oEvent* e )
{
	for (b2Body* b = _world.GetBodyList();b;b = b->GetNext())
	{
		b2Fixture* first = b->GetFixtureList();
		if (first)
		{
			first->Refilter();
			first = first->GetNext();
			for (b2Fixture* f = first;f && f != first;f->GetNext())
			{
				f->Refilter();
			}
		}
	}
}

float oWorld::b2Factor = 100.0f;

#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
void oWorld::onKey( uint8 key, bool isDown )
{
	oKeyboard::updateKey(key, isDown);
}
#endif

void oContactListener::PostSolve( b2Contact* contact, const b2ContactImpulse* impulse )
{ }

void oContactListener::PreSolve( b2Contact* contact, const b2Manifold* oldManifold )
{ }

void oContactListener::BeginContact( b2Contact* contact )
{
	b2Fixture* fixtureA = contact->GetFixtureA();
	b2Fixture* fixtureB = contact->GetFixtureB();
	oBody* bodyA = (oBody*)fixtureA->GetBody()->GetUserData();
	oBody* bodyB = (oBody*)fixtureB->GetBody()->GetUserData();
	if (!bodyA || !bodyB)
	{
		return;
	}
	if (fixtureA->IsSensor())
	{
		oSensor* sensor = (oSensor*)fixtureA->GetUserData();
		if (sensor && sensor->getEnable() && !fixtureB->IsSensor())
		{
			oSensorPair pair = {sensor, bodyB};
			pair.retain();
			g_sensorEnters.push_back(pair);
		}
	}
	else if (fixtureB->IsSensor())
	{
		oSensor* sensor = (oSensor*)fixtureB->GetUserData();
		if (sensor && sensor->getEnable())
		{
			oSensorPair pair = {sensor, bodyA};
			pair.retain();
			g_sensorEnters.push_back(pair);
		}
	}
}

void oContactListener::EndContact( b2Contact* contact )
{
	b2Fixture* fixtureA = contact->GetFixtureA();
	b2Fixture* fixtureB = contact->GetFixtureB();
	if (fixtureA->IsSensor())
	{
		oSensor* sensor = (oSensor*)fixtureA->GetUserData();
		oBody* bodyB = (oBody*)fixtureB->GetBody()->GetUserData();
		if (sensor && bodyB && sensor->getEnable() && !fixtureB->IsSensor())
		{
			oSensorPair pair = {sensor, bodyB};
			pair.retain();
			g_sensorLeaves.push_back(pair);
		}
	}
	else if (fixtureB->IsSensor())
	{
		oSensor* sensor = (oSensor*)fixtureB->GetUserData();
		oBody* bodyA = (oBody*)fixtureA->GetBody()->GetUserData();
		if (sensor && bodyA && sensor->getEnable())
		{
			oSensorPair pair = {sensor, bodyA};
			pair.retain();
			g_sensorLeaves.push_back(pair);
		}
	}
}

bool oContactFilter::ShouldCollide( b2Fixture* fixtureA, b2Fixture* fixtureB )
{
	const b2Filter* filterA = fixtureA->GetFilterData();
	const b2Filter* filterB = fixtureB->GetFilterData();
	bool collide = (filterA->getMaskBits() & filterB->getCategoryBits()) && (filterA->getCategoryBits() & filterB->getMaskBits());
	return collide;
}
